﻿namespace HutongGames.PlayMakerEditor
{
    using HutongGames.PlayMaker;
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using UnityEditor;
    using UnityEngine;

    internal static class FsmErrorChecker
    {
        private static object[] attributes;
        private static bool checkForErrors;
        private static Fsm checkFsm;
        private static FsmStateAction checkingAction;
        private static Fsm checkingFsm;
        private static string checkingParameter;
        private static FsmState checkingState;
        private const string CollisionEventsNeedCollider = "Owner needs a Collider/RigidBody to respond to collision/trigger events!";
        private const string ControllerCollisionEventsNeedController = "Owner needs a Character Controller to respond to Controller Collision events!";
        private const string DuplicateTransitionEventError = "An event is used more than once!";
        private static readonly List<FsmError> FsmErrorList = new List<FsmError>();
        private static FsmEventTarget fsmEventTargetContext;
        private static FsmEventTarget fsmEventTargetContextGlobal;
        private const string InvalidEventError = "Event not used by this state or any global transition!";
        private const string MissingActionError = "Bad Actions Array! Missing Action!";
        private const string MouseEventsNeedCollider = "Owner needs a Collider or GUI Element to respond to mouse events!";
        private static GameObject owner;
        private static PrefabType ownerPrefabType;
        private const string PrefabReferencingHierarchyError = "Prefabs should not reference scene objects, only other prefabs and project resources. References to scene objects will be lost on save/load!";
        private const string RequiredFieldError = "Required field...";
        private const string RequiresComponentError = "GameObject requires ";
        private const string TargetFsmMissingEventError = "Event not used by target FSM!";
        private const string TransitionMissingEventError = "Transition missing event!";
        private const string TransitionMissingTargetError = "Transition missing target state!";

        public static bool ActionHasErrors(FsmStateAction action)
        {
            foreach (FsmError error in FsmErrorList)
            {
                if (error.Action == action)
                {
                    return true;
                }
            }
            return false;
        }

        private static FsmError AddError(FsmError error)
        {
            foreach (FsmError error2 in FsmErrorList)
            {
                if (error2.SameAs(error))
                {
                    return error;
                }
            }
            FsmErrorList.Add(error);
            return error;
        }

        private static FsmError AddError(FsmState state, FsmTransition transition, string error)
        {
            return AddError(new FsmError(state, transition, error));
        }

        private static FsmError AddError(FsmState state, FsmStateAction action, string parameter, string error)
        {
            return AddError(new FsmError(state, action, parameter, error));
        }

        private static FsmError AddParameterError(string error)
        {
            return AddError(checkingState, checkingAction, checkingParameter, error);
        }

        private static FsmError AddRequiredFieldError()
        {
            FsmError error = AddError(checkingState, checkingAction, checkingParameter, "Required field...");
            error.Type = FsmError.ErrorType.requiredField;
            return error;
        }

        public static FsmError AddRuntimeError(string error)
        {
            FsmError error3 = new FsmError();
            error3.Fsm = Fsm.ExecutingFsm;
            error3.State = Fsm.ExecutingState;
            error3.Action = Fsm.ExecutingAction;
            error3.ErrorString = error;
            error3.RuntimeError = true;
            FsmError error2 = error3;
            return AddError(error2);
        }

        private static void CheckActionForErrors(FsmState state, FsmStateAction action)
        {
            if (action == null)
            {
                AddError(new FsmError(state, action, "Bad Actions Array! Missing Action!"));
            }
            else
            {
                fsmEventTargetContext = null;
                string actionLabel = FsmEditorUtility.GetActionLabel(action);
                if (FsmEditorSettings.CheckForObsoleteActions)
                {
                    string obsoleteAttribute = FsmEditorUtility.GetObsoleteAttribute(action.GetType());
                    if (!string.IsNullOrEmpty(obsoleteAttribute))
                    {
                        AddError(new FsmError(state, action, obsoleteAttribute));
                    }
                }
                System.Type actionType = action.GetType();
                foreach (FieldInfo info in ActionData.GetFields(actionType))
                {
                    System.Type fieldType = info.FieldType;
                    object obj2 = info.GetValue(action);
                    if (fieldType == typeof(FsmEventTarget))
                    {
                        FsmEventTarget target = (FsmEventTarget) obj2;
                        if (actionLabel == "Set Event Target")
                        {
                            fsmEventTargetContextGlobal = target;
                        }
                        else
                        {
                            fsmEventTargetContext = target;
                        }
                    }
                    CheckActionParameter(state, action, info);
                }
                string str3 = "";
                try
                {
                    str3 = action.ErrorCheck();
                }
                catch
                {
                    Debug.Log("Bad ErrorCheck: " + actionType);
                }
                if (!string.IsNullOrEmpty(str3))
                {
                    AddError(new FsmError(state, action, str3));
                }
            }
        }

        private static void CheckActionParameter(FsmState state, FsmStateAction action, FieldInfo field)
        {
            if (((((state != null) && (action != null)) && (field != null)) && (state.Fsm != null)) && (state.Fsm.GameObject != null))
            {
                ownerPrefabType = EditorUtility.GetPrefabType(state.Fsm.GameObject);
                owner = state.Fsm.GameObject;
                checkingFsm = state.Fsm;
                checkingState = state;
                checkingAction = action;
                checkingParameter = field.Name;
                System.Type fieldType = field.FieldType;
                object fieldValue = field.GetValue(action);
                attributes = field.GetCustomAttributes(true);
                CheckParameterType(fieldType, fieldValue);
            }
        }

        private static void CheckBaseGameObject(GameObject go)
        {
            if (go != null)
            {
                if (FsmEditorSettings.CheckForRequiredComponent)
                {
                    CheckForRequiredComponents(go);
                }
                if (FsmEditorSettings.CheckForPrefabRestrictions)
                {
                    CheckPrefabRestrictions(go);
                }
            }
        }

        public static void CheckForErrors()
        {
            checkForErrors = true;
        }

        private static string CheckForEventErrors(FsmEvent fsmEvent)
        {
            if (!FsmEvent.IsNullOrEmpty(fsmEvent))
            {
                FsmEventTarget fsmEventTargetContextGlobal = FsmErrorChecker.fsmEventTargetContextGlobal;
                if (fsmEventTargetContext != null)
                {
                    fsmEventTargetContextGlobal = fsmEventTargetContext;
                }
                if (fsmEventTargetContextGlobal == null)
                {
                    fsmEventTargetContextGlobal = new FsmEventTarget();
                }
                Fsm fsmTarget = FsmEditorUtility.GetFsmTarget(checkingFsm, fsmEventTargetContextGlobal);
                switch (fsmEventTargetContextGlobal.target)
                {
                    case FsmEventTarget.EventTarget.Self:
                        if ((checkingState == null) || FsmEditorUtility.FsmStateRespondsToEvent(checkingState, fsmEvent))
                        {
                            return null;
                        }
                        return "Event not used by this state or any global transition!";

                    case FsmEventTarget.EventTarget.GameObject:
                        return null;

                    case FsmEventTarget.EventTarget.GameObjectFSM:
                        return null;

                    case FsmEventTarget.EventTarget.FSMComponent:
                        if ((fsmTarget == null) || FsmEditorUtility.FsmRespondsToEvent(fsmTarget, fsmEvent))
                        {
                            return null;
                        }
                        return "Event not used by target FSM!";

                    case FsmEventTarget.EventTarget.BroadcastAll:
                        return null;
                }
            }
            return null;
        }

        private static void CheckForRequiredComponents(GameObject go)
        {
            if (FsmEditorSettings.CheckForRequiredComponent && (go != null))
            {
                foreach (Attribute attribute in attributes)
                {
                    CheckForComponentAttribute attribute2 = attribute as CheckForComponentAttribute;
                    if (attribute2 != null)
                    {
                        CheckGameObjectHasComponent(go, attribute2.Type0);
                        CheckGameObjectHasComponent(go, attribute2.Type1);
                        CheckGameObjectHasComponent(go, attribute2.Type2);
                    }
                }
            }
        }

        private static void CheckFsmEventParameter(FsmEvent fsmEvent)
        {
            if ((FsmEditorSettings.CheckForRequiredField && (fsmEvent == null)) && IsRequiredField())
            {
                AddRequiredFieldError();
            }
            else if (FsmEditorSettings.CheckForEventNotUsed)
            {
                string str = CheckForEventErrors(fsmEvent);
                if (!string.IsNullOrEmpty(str))
                {
                    AddParameterError(str);
                }
            }
        }

        public static void CheckFsmForErrors(Fsm fsm)
        {
            if (fsm != null)
            {
                checkFsm = fsm;
            }
        }

        private static void CheckFsmGameObjectParameter(FsmGameObject fsmGameObject)
        {
            if (fsmGameObject == null)
            {
                fsmGameObject = new FsmGameObject(string.Empty);
            }
            if (fsmGameObject.UseVariable)
            {
                if ((FsmEditorSettings.CheckForRequiredField && string.IsNullOrEmpty(fsmGameObject.Name)) && IsRequiredField())
                {
                    AddRequiredFieldError();
                }
                else
                {
                    CheckBaseGameObject(fsmGameObject.Value);
                }
            }
            else if ((FsmEditorSettings.CheckForRequiredField && (fsmGameObject.Value == null)) && IsRequiredField())
            {
                AddRequiredFieldError();
            }
            else
            {
                CheckBaseGameObject(fsmGameObject.Value);
            }
        }

        private static void CheckFsmStringParameter(FsmString fsmString)
        {
            if ((fsmString != null) && !fsmString.UseVariable)
            {
                CheckStringParameter(fsmString.Value);
            }
        }

        private static void CheckGameObjectHasComponent(GameObject go, System.Type component)
        {
            if (((go != null) && (component != null)) && (go.GetComponent(component) == null))
            {
                FsmError error = AddParameterError("GameObject requires " + FsmEditorUtility.StripUnityEngineNamespace(component.ToString()) + " Component!");
                error.Type = FsmError.ErrorType.missingRequiredComponent;
                error.GameObject = go;
                error.ObjectType = component;
            }
        }

        private static void CheckGameObjectParameter(GameObject go)
        {
            if (go == null)
            {
                if (FsmEditorSettings.CheckForRequiredField && IsRequiredField())
                {
                    AddRequiredFieldError();
                }
            }
            else
            {
                CheckBaseGameObject(go);
            }
        }

        private static void CheckObjectParameter(UnityEngine.Object unityObject)
        {
        }

        private static void CheckOwnerDefaultParameter(FsmOwnerDefault ownerDefault)
        {
            if (ownerDefault == null)
            {
                ownerDefault = new FsmOwnerDefault();
            }
            if (ownerDefault.OwnerOption == OwnerDefaultOption.UseOwner)
            {
                CheckBaseGameObject(owner);
            }
            else
            {
                CheckFsmGameObjectParameter(ownerDefault.GameObject);
            }
        }

        private static void CheckParameterType(System.Type type, object fieldValue)
        {
            if (type != null)
            {
                if (type == typeof(FsmGameObject))
                {
                    CheckFsmGameObjectParameter((FsmGameObject) fieldValue);
                }
                else if (type == typeof(FsmOwnerDefault))
                {
                    CheckOwnerDefaultParameter((FsmOwnerDefault) fieldValue);
                }
                else if (type == typeof(GameObject))
                {
                    CheckGameObjectParameter((GameObject) fieldValue);
                }
                else if (type == typeof(FsmEvent))
                {
                    CheckFsmEventParameter((FsmEvent) fieldValue);
                }
                else if (type == typeof(FsmString))
                {
                    CheckFsmStringParameter((FsmString) fieldValue);
                }
                else if (type == typeof(string))
                {
                    CheckStringParameter((string) fieldValue);
                }
                else if (type.IsArray)
                {
                    Array array = (Array) fieldValue;
                    if (array != null)
                    {
                        System.Type elementType = type.GetElementType();
                        for (int i = 0; i < array.Length; i++)
                        {
                            CheckParameterType(elementType, array.GetValue(i));
                        }
                    }
                }
                else
                {
                    UnityEngine.Object unityObject = fieldValue as UnityEngine.Object;
                    if (unityObject != null)
                    {
                        CheckObjectParameter(unityObject);
                    }
                }
                if ((type.IsSubclassOf(typeof(NamedVariable)) && FsmEditorSettings.CheckForRequiredField) && IsRequiredField())
                {
                    if (fieldValue == null)
                    {
                        AddRequiredFieldError();
                    }
                    else
                    {
                        NamedVariable variable = (NamedVariable) fieldValue;
                        if ((variable.UseVariable || IsVariableField()) && string.IsNullOrEmpty(variable.Name))
                        {
                            AddRequiredFieldError();
                        }
                    }
                }
            }
        }

        private static void CheckPrefabRestrictions(GameObject go)
        {
            if ((go != null) && (ownerPrefabType == PrefabType.Prefab))
            {
                PrefabType prefabType = EditorUtility.GetPrefabType(go);
                if ((prefabType != PrefabType.Prefab) && (prefabType != PrefabType.ModelPrefab))
                {
                    AddParameterError("Prefabs should not reference scene objects, only other prefabs and project resources. References to scene objects will be lost on save/load!");
                }
            }
        }

        private static void CheckStringParameter(string text)
        {
            if ((FsmEditorSettings.CheckForRequiredField && string.IsNullOrEmpty(text)) && IsRequiredField())
            {
                AddRequiredFieldError();
            }
        }

        private static void CheckSystemEventsForErrors(FsmState state, FsmTransition transition, FsmEvent fsmEvent)
        {
            GameObject gameObject = state.Fsm.GameObject;
            if (gameObject != null)
            {
                if ((FsmEditorSettings.CheckForMouseEventErrors && fsmEvent.Name.Contains("MOUSE")) && ((gameObject.GetComponent<Collider>() == null) && (gameObject.GetComponent<GUIElement>() == null)))
                {
                    AddError(state, transition, "Owner needs a Collider or GUI Element to respond to mouse events!");
                }
                if (((FsmEditorSettings.CheckForCollisionEventErrors && fsmEvent.Name.Contains("COLLISION")) || fsmEvent.Name.Contains("TRIGGER")) && ((gameObject.GetComponent<Collider>() == null) && (gameObject.GetComponent<Rigidbody>() == null)))
                {
                    AddError(state, transition, "Owner needs a Collider/RigidBody to respond to collision/trigger events!");
                }
                if ((FsmEditorSettings.CheckForCollisionEventErrors && fsmEvent.Name.Contains("CONTROLLER COLLIDER")) && (gameObject.GetComponent<CharacterController>() == null))
                {
                    AddError(state, transition, "Owner needs a Character Controller to respond to Controller Collision events!");
                }
            }
        }

        private static void CheckTransitionsForErrors(FsmState state)
        {
            List<string> list = new List<string>();
            foreach (FsmTransition transition in state.Transitions)
            {
                if (FsmEditorSettings.CheckForTransitionMissingEvent && string.IsNullOrEmpty(transition.EventName))
                {
                    AddError(state, transition, "Transition missing event!");
                }
                if (FsmEditorSettings.CheckForDuplicateTransitionEvent && list.Contains(transition.EventName))
                {
                    AddError(state, transition, "An event is used more than once!");
                }
                if (!string.IsNullOrEmpty(transition.EventName))
                {
                    list.Add(transition.EventName);
                }
                if (FsmEditorSettings.CheckForTransitionMissingTarget && string.IsNullOrEmpty(transition.ToState))
                {
                    AddError(state, transition, "Transition missing target state!");
                }
                if (state.Fsm != null)
                {
                    FsmEvent fsmEvent = transition.FsmEvent;
                    if ((fsmEvent != null) && fsmEvent.IsSystemEvent)
                    {
                        CheckSystemEventsForErrors(state, transition, fsmEvent);
                    }
                }
            }
        }

        public static void ClearErrors([Optional, DefaultParameterValue(true)] bool deleteRuntimeErrors)
        {
            if (!Application.isPlaying && !deleteRuntimeErrors)
            {
                List<FsmError> list = new List<FsmError>();
                foreach (FsmError error in FsmErrorList)
                {
                    if (!error.RuntimeError)
                    {
                        list.Add(error);
                    }
                }
                foreach (FsmError error2 in list)
                {
                    FsmErrorList.Remove(error2);
                }
            }
            else
            {
                FsmErrorList.Clear();
            }
        }

        private static void ClearFsmErrors(Fsm fsm)
        {
            List<FsmError> list = new List<FsmError>();
            foreach (FsmError error in FsmErrorList)
            {
                if ((error.Fsm == fsm) && !error.RuntimeError)
                {
                    list.Add(error);
                }
                if (!FsmEditor.FsmList.Contains(error.Fsm))
                {
                    list.Add(error);
                }
            }
            foreach (FsmError error2 in list)
            {
                FsmErrorList.Remove(error2);
            }
        }

        public static int CountActionErrors(FsmStateAction action)
        {
            int num = 0;
            foreach (FsmError error in FsmErrorList)
            {
                if (error.Action == action)
                {
                    num++;
                }
            }
            return num;
        }

        public static int CountAllErrors()
        {
            return FsmErrorList.Count;
        }

        public static int CountFsmErrors(Fsm fsm)
        {
            int num = 0;
            foreach (FsmError error in FsmErrorList)
            {
                if (error.Fsm == fsm)
                {
                    num++;
                }
            }
            return num;
        }

        public static int CountRuntimeErrors()
        {
            int num = 0;
            foreach (FsmError error in FsmErrorList)
            {
                if (error.RuntimeError)
                {
                    num++;
                }
            }
            return num;
        }

        public static int CountSetupErrors()
        {
            int num = 0;
            foreach (FsmError error in FsmErrorList)
            {
                if (!error.RuntimeError)
                {
                    num++;
                }
            }
            return num;
        }

        public static int CountStateErrors(FsmState state)
        {
            int num = 0;
            foreach (FsmError error in FsmErrorList)
            {
                if (error.State == state)
                {
                    num++;
                }
            }
            return num;
        }

        private static void DoCheckForErrors()
        {
            ClearErrors(false);
            foreach (Fsm fsm in FsmEditor.FsmList)
            {
                DoCheckFsmForErrors(fsm);
            }
            FsmEditor.RepaintAll();
        }

        private static void DoCheckFsmForErrors(Fsm fsm)
        {
            if (fsm != null)
            {
                fsm.InitData();
                checkingFsm = fsm;
                ClearFsmErrors(fsm);
                foreach (FsmState state in fsm.States)
                {
                    fsmEventTargetContextGlobal = null;
                    foreach (FsmStateAction action in state.Actions)
                    {
                        if (action.Enabled)
                        {
                            CheckActionForErrors(state, action);
                        }
                    }
                    CheckTransitionsForErrors(state);
                }
                FsmEditor.RepaintAll();
            }
        }

        public static FsmError FindError(FsmError error)
        {
            foreach (FsmError error2 in FsmErrorList)
            {
                if (error2.SameAs(error))
                {
                    return error2;
                }
            }
            return null;
        }

        public static bool FsmHasErrors(Fsm fsm)
        {
            foreach (FsmError error in FsmErrorList)
            {
                if (error.Fsm == fsm)
                {
                    return true;
                }
            }
            return false;
        }

        public static List<FsmError> GetErrors()
        {
            return FsmErrorList;
        }

        public static List<FsmError> GetParameterErrors(FsmStateAction action, string parameter)
        {
            List<FsmError> list = new List<FsmError>();
            foreach (FsmError error in FsmErrorList)
            {
                if ((error.Action == action) && (error.Parameter == parameter))
                {
                    list.Add(error);
                }
            }
            return list;
        }

        public static List<string> GetRuntimeErrors(FsmStateAction action)
        {
            List<string> list = new List<string>();
            foreach (FsmError error in FsmErrorList)
            {
                if (error.RuntimeError && (error.Action == action))
                {
                    list.Add(error.ErrorString);
                }
            }
            return list;
        }

        public static string GetStateErrors(FsmState state)
        {
            string str = "";
            foreach (FsmError error in FsmErrorList)
            {
                if (error.State == state)
                {
                    str = str + error.ErrorString;
                }
            }
            return str;
        }

        public static List<string> GetTransitionErrors(FsmTransition transition)
        {
            List<string> list = new List<string>();
            foreach (FsmError error in FsmErrorList)
            {
                if (error.Transition == transition)
                {
                    list.Add(error.ErrorString);
                }
            }
            return list;
        }

        private static bool IsRequiredField()
        {
            foreach (Attribute attribute in attributes)
            {
                if (attribute is RequiredFieldAttribute)
                {
                    return true;
                }
            }
            return false;
        }

        private static bool IsVariableField()
        {
            foreach (Attribute attribute in attributes)
            {
                UIHintAttribute attribute2 = attribute as UIHintAttribute;
                if ((attribute2 != null) && (attribute2.Hint == UIHint.Variable))
                {
                    return true;
                }
            }
            return false;
        }

        public static void Refresh()
        {
            CheckForErrors();
        }

        public static bool StateHasErrors(FsmState state)
        {
            foreach (FsmError error in FsmErrorList)
            {
                if (error.State == state)
                {
                    return true;
                }
            }
            return false;
        }

        public static void Update()
        {
            if (((FsmEditorSettings.EnableRealtimeErrorChecker && !PlayMakerFSM.ApplicationIsQuitting) && (!FsmEditorSettings.DisableErrorCheckerWhenPlaying || !Application.isPlaying)) && !FsmEditor.NeedRepaint)
            {
                if (checkForErrors)
                {
                    DoCheckForErrors();
                    FsmSelector.Refresh();
                }
                else if (checkFsm != null)
                {
                    DoCheckFsmForErrors(checkFsm);
                    FsmSelector.Refresh();
                }
                checkForErrors = false;
                checkFsm = null;
            }
        }
    }
}

